package scala.examples.fs.parser;

import scala.util.parsing._;
import scala.examples.fs.syntax.Syntax._;
import java.io.FileInputStream;

abstract class FSParsers extends Parsers {
  def any: Parser[String];

  private var cnt = 0;

  private def newName = { cnt = cnt + 1; "x$" + cnt }

  def isKeyword(s: String) = s == "new" || s == "val" || s == "trait" || s == "extends" ||  s == "type" 

  def isIdent(s: String): boolean =
    s.length() > 0 && 
    { val ch = s.charAt(0);
     'A' <= ch && ch <= 'Z' || 'a' <= ch && ch <= 'z' || ch == '_' || ch == '$' } && 
    !isKeyword(s);

  def ident: Parser[String] = 
    for (val s <- any; isIdent(s)) yield s;

  def token(tok: String): Parser[String] = 
    for (val s <- any; s == tok) yield s;

  /** value = "unit" | ident
   */
  def value: Parser[Term] = 
    (for (val _ <- token("unit")) yield Unit: Term)
    (for (val n <- ident) yield Name(n): Term);
    
  def e[a](k :a) :Boolean = {System.err.println(k) ; true}


  /** expr = select | targetExpr  
   */
  def expr: Parser[Term] =  select ||| targetExpr
    
  /**
   * targetExpr = apply
   *            | path
   *            | newExpr
   *            | atomic
   *            
   */
  def targetExpr: Parser[Term] = 
    apply    |||
    path |||
    newExpr |||
    atomic 
   

  /**
   *  apply = path actuals
   */
  def apply: Parser[Term] = 
    (for (val fun <- path;val args <- actuals)
      yield MethodCall(fun, args): Term);   

  /**
   * actuals = "(" [expr {"," expr}] ")"
   */
  def actuals: Parser[List[Term]] = 
    (for (val _ <- token("("); val args <- commaSep(expr); val _ <- token(")"))
      yield args);   
  
 
  /**
   * select = targetExpr {"." apply}+
   */            
  def select: Parser[Term] = { 
    (for (val tgt <- targetExpr;   val apps <- rep1((token(".") &&& (apply ||| path))))  //; e(""+tgt+"**"+apps+"**")
        yield (tgt /: apps)((accum,el) => el match {
                     case Apply(sel, as) => Apply(prepend(accum, sel), as)
                     case _ => prepend(accum, el) // or, more verbosely:
//                     case Name(n) => Select(accum, n)
//                     case sel :Select => prepend(accum, sel)
        }): Term) 
    }


  /** path = value {"." ident}
   */
  def path: Parser[Term] = { 
    (for (val tgt <- value; val selectors <- rep(token(".") &&& ident)) //;e("p"+(tgt /: selectors) (Select))
        yield (tgt /: selectors) (Select): Term);
    }

   /** newExpr =  "new" path
    *            | "new" classSig 
    */
  def newExpr: Parser[Term] = {
    (for (val _ <- token("new"); val p <- path; val _ <- not(token("{")))
       yield New(p): Term) |||
    (for (val _ <- token("new"); val z = newName; val c <- classSig(Name(z))) // NEW
       yield {  ClassDef(z, c, New(Name(z))): Term }) 
  }

    
  /** term = "val" ident [":" typ]  "=" expr ";" term
   *       | "trait" ident "extends" classSig ";" term
   *       | expr
   */
  def term: Parser[Term] = 
    (for (val _ <- token("val"); val x <- ident; val tpe <- opt(token(":") &&& typ); val _ <- token("="); 
	  val e <- expr; val _ <- token(";"); val t <- term)
     yield LocalVal(x, tpe.find( x => true), e, t): Term) |||
    (for (val _ <- token("trait"); val x <- ident; val _ <- token("extends"); 
	  val c <- classSig(Name(x)); val _ <- token(";"); val t <- term)
     yield ClassDef(x, c, t): Term) |||
    expr
    
  /** atomic = "(" term ")"
   *         | "{" term "}"
   */
  def atomic: Parser[Term] = { 
     (for (val _ <- token("("); val t <- term; val _ <- token(")")) yield t) |||
     (for (val _ <- token("{"); val t <- term; val _ <- token("}")) yield t)
  }
     
  /** commaSep(x) = [x {"," x}] 
   */
  def commaSep[a](el: Parser[a]) : Parser[List[a]] =
    repWith(el, token(",")) ||| succeed(List());
   
  /** paths = [path {"," path}] 
   */
  def paths: Parser[List[Term]] = commaSep(path)

  /** optSelf = [ident "|"]
   */
  def optSelf: Parser[String] =
    (for (val x <- ident; val _ <- token("|")) yield x) ||| succeed("this");

  /** classSig = paths "{" optSelf {declaration | definition} "}"
   */
  def classSig(container: Term): Parser[ClassSig] =  // container should be a path to a class
    for (val ps <- paths; val _ <- token("{"); val x <- optSelf; 
         val ms <- rep(declaration ||| definition(Name(x))); 
	 val _ <- token("}"))
    yield ClassSig(ps, x, if(container == null) null else Inst(container), ms.filter(x => x.isInstanceOf[Declaration]) ::: ms.filter(x => x.isInstanceOf[Definition]));

  /** declaration = "val" ident ":" typ [";"]  (not followed by "=")
   *              | "def" ident [argsList] ":" typ [";"] (not followed by "=")
   *              | "type" ident ["<:" typ] [";"] (not followed by "=")  
   */
  def declaration: Parser[Member] = 
    (for (val _ <- token("val"); val l <- ident; val _ <- token(":"); val t <- typ;
          val _ <- not(token("=")); val _ <- opt(token(";")))
     yield ValDcl(l, t): Member) |||
    (for (val _ <- token("def"); val l <- ident; val args <- opt(argsList); val _ <- token(":"); val t <- typ; 
          val _ <- not(token("=")); val _ <- opt(token(";")))
      yield DefDcl(l, List.flatten(args), t): Member) ||| 
    (for (val _ <- token("type"); val l <- ident; val bound <- opt(token("<:") &&& typ); val _ <- not(token("=")); val _ <- opt(token(";")))
     yield TypeDcl(l, bound.find(x => true)): Member);
  
  // formal = ident ":" type 
  def formal: Parser[Arg] = 
    (for (val a <- ident; val _ <- token(":"); val t <- typ)
      yield Arg(a, t))
    
  
  // formals = [ formal {"," formals}]
  def formals: Parser[List[Arg]] =  commaSep(formal)
    
  // argsList = "(" args ")"
  def argsList: Parser[List[Arg]] = 
    (for (val _ <- token("("); val a <- formals; val _ <- token(")"))
    	yield a: List[Arg])
  
  /** definition = "val" ident ":" typ "=" expr [";"] 
   *             | "def" ident  [argsList] ":" typ "=" expr [";"]
   *             | "type" ident "=" typ [";"]
   *             | "trait" ident "extends" classSig [";"]
   */
  def definition(self: Term): Parser[Member] = 
    (for (val _ <- token("val"); val l <- ident; val _ <- token(":"); val t <- typ;
          val _ <- token("="); val e <- expr; val _ <- opt(token(";")))
     yield ValDef(l, t, e): Member) |||
    (for (val _ <- token("type"); val l <- ident; val _ <- token("="); val t <- typ;
          val _ <- opt(token(";")))
     yield TypeDef(l, t): Member) |||
    (for (val _ <- token("def"); val l <- ident; val args <- opt(argsList); val _ <- token(":"); val t <- typ;
       val _ <- token("=");  val ee <- expr; val _ <- opt(token(";"))) // e("def" + l + args);
      yield DefDef(l, List.flatten(args), t, ee): Member) |||
    (for (val _ <- token("trait"); val l <- ident; val _ <- token("extends"); 
	  val c <- classSig(Select(self, l)); val _ <- opt(token(";")))
     yield { val z = newName; ValDef(l, Class(c), ClassDef(z, c, Name(z))): Member });

  /** typ =  classSig
   *      | simpleTyp {"#" ident} // TODO: is type selection handled properly?
   */
  def typ: Parser[Type] = 
    (for (val s <- classSig(null)) 
     yield Class(s): Type) |||
    (for (val t <- simpleTyp; val selectors <- rep(token("#") &&& ident))
     yield (t /: selectors) (TypeSelect): Type);

  /** simpleTyp = path "." type
   *            | path
   */
  def simpleTyp: Parser[Type] = 
    (for (val p <- path; val _ <- token("."); val _ <- token("inst")) 
     yield Inst(p): Type) |||
    (for (val p <- path; val _ <- token("."); val _ <- token("type")) 
     yield Single(p): Type);
}

class FSParser extends FSParsers {
  val delimiters = ";.,:|(){}=";
  type inputType = Stream[String];

  def any = new Parser[String] {
    def apply(in: inputType): Result = 
      if (in.isEmpty) None else Some(Pair(in.head, in.tail));
  }

  def parse(in: Iterator[char]): Term = {
    val echoIn = in map { ch => System.err.print(ch); ch }
    val input = Stream.fromIterator(new FSTokenizer(in /*echoIn*/, delimiters));
    term(input) match {
      case None => System.err.println("***** syntax error"); null
      case Some(Pair(t, _)) => t
    }
  }
}

  class FSTokenizer(in: Iterator[char], delimiters: String) extends Iterator[String] {

    private def max(x: int, y: char): int = if (x > y) x else y;

    val tracing = false;

    private def delimArray: Array[boolean] = {
      val ds = List.fromString(delimiters);
      val da = new Array[boolean]((0 /: ds)(max) + 1);
      for (val ch <- ds) { da(ch) = true }
      da
    }

    private val isdelim = delimArray
    private def isDelimiter(ch: char) = ch < isdelim.length && isdelim(ch)
    private def isSpace(ch: char) = ch < ' '


    private var ch: char = {advance;peek}

    private val buf = new StringBuffer();
    private def clear =  buf.setLength(0)

    protected def hasNextCh: boolean = in.hasNext
    protected def peek :char = ch
    
    // @pre hasNextCh
    protected def advance :unit = ch = in.next.asInstanceOf[char]

    protected def consume :unit = {buf append peek; if(hasNextCh) advance}
    protected def skip :unit = if(hasNextCh) advance
    protected def commit :String= {val r = buf.toString;clear; r}
    protected def discard :unit = clear

    case class ScanResult(token :String, next :Scanner)
    
    type Scanner = Function0[Option[ScanResult]] // apply: @pre hasNextCh
      
    // TODO: scanner makes for nice testbed for responder impl in Scala 
    // TODO: abstract these while loops into a nice monadic/idiomatic for-loop which handles end-of-stream (and advancing the cursor)
    
    private def doFirst(ss :Scanner*) :Scanner = new Scanner {
      def apply :Option[ScanResult] = { 
        val it = ss.elements
        while(it.hasNext && hasNextCh) {
          val e = it.next
          e() match {
            case r @ Some(_) => 
                return r
            case _ =>
          }
        }
        
        return None
      }
    }


    private def delimString(start :Boolean) :Scanner = new Scanner { def apply :Option[ScanResult] = {  
        if(peek == '"') {
          consume
          return Some(ScanResult(commit, if(start) doFirst(delimString(false),inString) else defaultScanner))
        } else  None
      }
    }
    
    private def inString :Scanner = new Scanner { def apply :Option[ScanResult] = { 
        do{
          consume
        }while(peek != '"'  && hasNextCh )     

        return Some(ScanResult(commit, doFirst(delimString(false),inString)))
      }
    }

    private def comment[t] :Scanner = new Scanner { def apply :Option[ScanResult] = {       
        if(peek == '/') {
         consume

         if(peek == '/') {
            consume
            while (ch != '\n' && hasNextCh) { consume }
            
            discard

            return defaultScanner() 
          } else None
        }
        else None
      }
    }
      
      
 
    private def scalaToken :Scanner = new Scanner { def apply :Option[ScanResult] = { 
          if (isDelimiter(ch)) {
            consume
          } else {
            while (peek > ' ' && hasNextCh && !isDelimiter(peek)) {
              consume
            }
          }

          return Some(ScanResult(commit, defaultScanner))
      }
    }

    private def whiteSpace :Scanner = new Scanner { def apply :Option[ScanResult] = { 
        if(peek <= ' '){
          while (peek <= ' ' && hasNextCh) skip
          discard
          return defaultScanner()
        } else None
      }
    }
    
    private def defaultScanner :Scanner = doFirst(delimString(true), comment, whiteSpace, scalaToken)
    
    private var scanner :Scanner = defaultScanner
    
    var lastResult :ScanResult = _
    
    def hasNext: boolean = scanner() match {
            case Some(result) => lastResult=result; true
            case None => false
          }
    
    // @pre hasNext must have been called
    def next: String = {
       val ScanResult(r, next) = lastResult
       scanner = next
         
       if (tracing) System.out.println("<" + r + ">");
       r
    }
  }



/*object ParserTest extends Parser {

  def main(args: Array[String]): unit = {
    val t = parse(new CharInputStreamIterator(new FileInputStream(args(0))));
    if (t != null) {
      System.out.println("[generated tree:]\n"); 
      System.out.println(show.term(t));
      System.out.println("\n[end of generated tree]"); 
    }
  }
}*/
    
    
